{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0iWrV_uw-Ay3",
        "outputId": "0cfdd236-4567-4628-efd7-5483c408aded"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "=== GOOD DATACLASS EXAMPLES ===\n",
            "Person 1: Person(name='Alice', age=30, email='alice@example.com', tags=['developer'])\n",
            "Person 2: Person(name='Bob', age=25, email=None, tags=[])\n",
            "Person 3: Person(name='Charlie', age=17, email=None, tags=['student', 'part-time'])\n",
            "Is Alice an adult? True\n",
            "Is Charlie an adult? False\n",
            "\n",
            "=== BAD REGULAR CLASS EXAMPLES ===\n",
            "PersonBad 1: PersonBad(name=Alice, age=30, email=alice@example.com, tags=[])\n",
            "PersonBad 2: PersonBad(name=Bob, age=25, email=None, tags=[])\n"
          ]
        }
      ],
      "source": [
        "\"\"\"\n",
        "01_basic_dataclasses.py - Basic usage of dataclasses\n",
        "\n",
        "This file demonstrates the proper use of dataclasses for simple data structures.\n",
        "\"\"\"\n",
        "\n",
        "from dataclasses import dataclass, field\n",
        "from typing import List, Optional\n",
        "\n",
        "\n",
        "# GOOD EXAMPLE: Simple dataclass with type hints\n",
        "@dataclass\n",
        "class Person:\n",
        "    name: str\n",
        "    age: int\n",
        "    email: Optional[str] = None\n",
        "    # Using field() with default_factory for mutable default values\n",
        "    tags: List[str] = field(default_factory=list)\n",
        "\n",
        "    def is_adult(self) -> bool:\n",
        "        \"\"\"Example method that uses the dataclass attributes.\"\"\"\n",
        "        return self.age >= 18\n",
        "\n",
        "\n",
        "# Usage example\n",
        "def demo_good_usage():\n",
        "    # Creating instances\n",
        "    person1 = Person(name=\"Alice\", age=30, email=\"alice@example.com\")\n",
        "    person2 = Person(name=\"Bob\", age=25)\n",
        "    person3 = Person(name=\"Charlie\", age=17, tags=[\"student\", \"part-time\"])\n",
        "\n",
        "    # Adding to a mutable field\n",
        "    person1.tags.append(\"developer\")\n",
        "\n",
        "    # Using the built-in string representation\n",
        "    print(f\"Person 1: {person1}\")\n",
        "    print(f\"Person 2: {person2}\")\n",
        "    print(f\"Person 3: {person3}\")\n",
        "\n",
        "    # Using the instance method\n",
        "    print(f\"Is {person1.name} an adult? {person1.is_adult()}\")\n",
        "    print(f\"Is {person3.name} an adult? {person3.is_adult()}\")\n",
        "\n",
        "\n",
        "# BAD EXAMPLE: Class without dataclass\n",
        "class PersonBad:\n",
        "    def __init__(self, name, age, email=None, tags=None):\n",
        "        self.name = name\n",
        "        self.age = age\n",
        "        self.email = email\n",
        "        # Common mistake: mutable default\n",
        "        self.tags = tags if tags is not None else []\n",
        "\n",
        "    # Have to manually define string representation\n",
        "    def __repr__(self):\n",
        "        return f\"PersonBad(name={self.name}, age={self.age}, email={self.email}, tags={self.tags})\"\n",
        "\n",
        "    # Have to manually define equality\n",
        "    def __eq__(self, other):\n",
        "        if not isinstance(other, PersonBad):\n",
        "            return False\n",
        "        return (self.name == other.name and\n",
        "                self.age == other.age and\n",
        "                self.email == other.email and\n",
        "                self.tags == other.tags)\n",
        "\n",
        "\n",
        "def demo_bad_usage():\n",
        "    # More verbose and error-prone without dataclasses\n",
        "    person1 = PersonBad(\"Alice\", 30, \"alice@example.com\")\n",
        "    person2 = PersonBad(\"Bob\", 25)\n",
        "\n",
        "    print(f\"PersonBad 1: {person1}\")\n",
        "    print(f\"PersonBad 2: {person2}\")\n",
        "\n",
        "\n",
        "if __name__ == \"__main__\":\n",
        "    print(\"=== GOOD DATACLASS EXAMPLES ===\")\n",
        "    demo_good_usage()\n",
        "\n",
        "    print(\"\\n=== BAD REGULAR CLASS EXAMPLES ===\")\n",
        "    demo_bad_usage()"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "\"\"\"\n",
        "02_nested_dataclasses.py - Working with nested dataclasses\n",
        "\n",
        "This file demonstrates how to properly structure and work with nested dataclasses.\n",
        "\"\"\"\n",
        "\n",
        "from dataclasses import dataclass, field, asdict\n",
        "from typing import List, Dict, Optional\n",
        "import json\n",
        "\n",
        "\n",
        "# GOOD EXAMPLE: Well-structured nested dataclasses\n",
        "@dataclass\n",
        "class Address:\n",
        "    street: str\n",
        "    city: str\n",
        "    state: str\n",
        "    zip_code: str\n",
        "    country: str = \"USA\"\n",
        "\n",
        "\n",
        "@dataclass\n",
        "class Contact:\n",
        "    email: str\n",
        "    phone: Optional[str] = None\n",
        "\n",
        "\n",
        "@dataclass\n",
        "class Employee:\n",
        "    id: int\n",
        "    name: str\n",
        "    department: str\n",
        "    # Nested dataclass as a field\n",
        "    address: Address\n",
        "    # Another nested dataclass\n",
        "    contact: Contact\n",
        "    # List of another dataclass type\n",
        "    skills: List[str] = field(default_factory=list)\n",
        "\n",
        "    def to_json(self) -> str:\n",
        "        \"\"\"Convert the employee data to JSON string.\"\"\"\n",
        "        # asdict recursively converts dataclasses to dictionaries\n",
        "        return json.dumps(asdict(self), indent=2)\n",
        "\n",
        "    def add_skill(self, skill: str) -> None:\n",
        "        \"\"\"Add a skill to the employee's skill list.\"\"\"\n",
        "        if skill not in self.skills:\n",
        "            self.skills.append(skill)\n",
        "\n",
        "\n",
        "# Usage example - Good pattern\n",
        "def demo_good_nested():\n",
        "    # Create nested dataclass instances\n",
        "    address = Address(\n",
        "        street=\"123 Tech Lane\",\n",
        "        city=\"San Francisco\",\n",
        "        state=\"CA\",\n",
        "        zip_code=\"94107\"\n",
        "    )\n",
        "\n",
        "    contact = Contact(\n",
        "        email=\"john.doe@example.com\",\n",
        "        phone=\"555-123-4567\"\n",
        "    )\n",
        "\n",
        "    # Create the parent dataclass with nested instances\n",
        "    employee = Employee(\n",
        "        id=1001,\n",
        "        name=\"John Doe\",\n",
        "        department=\"Engineering\",\n",
        "        address=address,\n",
        "        contact=contact,\n",
        "        skills=[\"Python\", \"Data Science\"]\n",
        "    )\n",
        "\n",
        "    # Access nested attributes with proper dot notation\n",
        "    print(f\"Employee: {employee.name}\")\n",
        "    print(f\"City: {employee.address.city}\")\n",
        "    print(f\"Email: {employee.contact.email}\")\n",
        "\n",
        "    # Add a skill\n",
        "    employee.add_skill(\"Machine Learning\")\n",
        "    print(f\"Skills: {employee.skills}\")\n",
        "\n",
        "    # Convert to JSON\n",
        "    print(\"\\nEmployee JSON:\")\n",
        "    print(employee.to_json())\n",
        "\n",
        "\n",
        "# BAD EXAMPLE: Poorly structured data without proper nesting\n",
        "@dataclass\n",
        "class EmployeeBad:\n",
        "    id: int\n",
        "    name: str\n",
        "    department: str\n",
        "    # Flat structure instead of proper nesting\n",
        "    street: str\n",
        "    city: str\n",
        "    state: str\n",
        "    zip_code: str\n",
        "    email: str\n",
        "    # Fields with default values must come after required fields\n",
        "    phone: Optional[str] = None\n",
        "    country: str = \"USA\"\n",
        "    skills: List[str] = field(default_factory=list)\n",
        "\n",
        "\n",
        "# Even worse example: using dictionaries instead of proper dataclasses\n",
        "class EmployeeWorse:\n",
        "    def __init__(self, id, name, department, address_dict, contact_dict, skills=None):\n",
        "        self.id = id\n",
        "        self.name = name\n",
        "        self.department = department\n",
        "        # Using dictionaries instead of proper dataclasses\n",
        "        self.address = address_dict  # {\"street\": \"...\", \"city\": \"...\", ...}\n",
        "        self.contact = contact_dict  # {\"email\": \"...\", \"phone\": \"...\"}\n",
        "        self.skills = skills or []\n",
        "\n",
        "\n",
        "def demo_bad_nested():\n",
        "    # Flat structure makes it harder to organize and maintain\n",
        "    employee_bad = EmployeeBad(\n",
        "        id=1001,\n",
        "        name=\"John Doe\",\n",
        "        department=\"Engineering\",\n",
        "        street=\"123 Tech Lane\",\n",
        "        city=\"San Francisco\",\n",
        "        state=\"CA\",\n",
        "        zip_code=\"94107\",\n",
        "        email=\"john.doe@example.com\",\n",
        "        phone=\"555-123-4567\",\n",
        "        skills=[\"Python\", \"Data Science\"]\n",
        "    )\n",
        "\n",
        "    print(\"\\n=== BAD FLAT STRUCTURE ===\")\n",
        "    print(f\"Employee: {employee_bad}\")\n",
        "\n",
        "    # Using dictionaries is even worse\n",
        "    employee_worse = EmployeeWorse(\n",
        "        id=1001,\n",
        "        name=\"John Doe\",\n",
        "        department=\"Engineering\",\n",
        "        address_dict={\n",
        "            \"street\": \"123 Tech Lane\",\n",
        "            \"city\": \"San Francisco\",\n",
        "            \"state\": \"CA\",\n",
        "            \"zip_code\": \"94107\"\n",
        "        },\n",
        "        contact_dict={\n",
        "            \"email\": \"john.doe@example.com\",\n",
        "            \"phone\": \"555-123-4567\"\n",
        "        },\n",
        "        skills=[\"Python\", \"Data Science\"]\n",
        "    )\n",
        "\n",
        "    print(\"\\n=== WORSE DICTIONARY APPROACH ===\")\n",
        "    # No nice string representation\n",
        "    print(f\"Employee: {employee_worse.__dict__}\")\n",
        "    # Error-prone access to nested data\n",
        "    print(f\"City: {employee_worse.address['city']}\")\n",
        "\n",
        "\n",
        "if __name__ == \"__main__\":\n",
        "    print(\"=== GOOD NESTED DATACLASS EXAMPLE ===\")\n",
        "    demo_good_nested()\n",
        "\n",
        "    print(\"\\n=== BAD NESTED DATACLASS EXAMPLES ===\")\n",
        "    demo_bad_nested()"
      ],
      "metadata": {
        "id": "vqT8KcAkTYzi",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "a8dfd6fe-4fd0-45a0-bd05-6dd0a33b7126"
      },
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "=== GOOD NESTED DATACLASS EXAMPLE ===\n",
            "Employee: John Doe\n",
            "City: San Francisco\n",
            "Email: john.doe@example.com\n",
            "Skills: ['Python', 'Data Science', 'Machine Learning']\n",
            "\n",
            "Employee JSON:\n",
            "{\n",
            "  \"id\": 1001,\n",
            "  \"name\": \"John Doe\",\n",
            "  \"department\": \"Engineering\",\n",
            "  \"address\": {\n",
            "    \"street\": \"123 Tech Lane\",\n",
            "    \"city\": \"San Francisco\",\n",
            "    \"state\": \"CA\",\n",
            "    \"zip_code\": \"94107\",\n",
            "    \"country\": \"USA\"\n",
            "  },\n",
            "  \"contact\": {\n",
            "    \"email\": \"john.doe@example.com\",\n",
            "    \"phone\": \"555-123-4567\"\n",
            "  },\n",
            "  \"skills\": [\n",
            "    \"Python\",\n",
            "    \"Data Science\",\n",
            "    \"Machine Learning\"\n",
            "  ]\n",
            "}\n",
            "\n",
            "=== BAD NESTED DATACLASS EXAMPLES ===\n",
            "\n",
            "=== BAD FLAT STRUCTURE ===\n",
            "Employee: EmployeeBad(id=1001, name='John Doe', department='Engineering', street='123 Tech Lane', city='San Francisco', state='CA', zip_code='94107', email='john.doe@example.com', phone='555-123-4567', country='USA', skills=['Python', 'Data Science'])\n",
            "\n",
            "=== WORSE DICTIONARY APPROACH ===\n",
            "Employee: {'id': 1001, 'name': 'John Doe', 'department': 'Engineering', 'address': {'street': '123 Tech Lane', 'city': 'San Francisco', 'state': 'CA', 'zip_code': '94107'}, 'contact': {'email': 'john.doe@example.com', 'phone': '555-123-4567'}, 'skills': ['Python', 'Data Science']}\n",
            "City: San Francisco\n"
          ]
        }
      ]
    }
  ]
}